@using System
@using System.Collections.Generic
@using System.Globalization
@using System.Linq
@using Microsoft.AspNetCore.Diagnostics.Elm
@using Microsoft.AspNetCore.Diagnostics.Elm.Views
@using Microsoft.AspNetCore.DiagnosticsViewPage.Views
@using Microsoft.Extensions.Logging

@functions
{
    public LogPage(LogPageModel model)
    {
        Model = model;
    }

    public LogPageModel Model { get; set; }

    public HelperResult LogRow(LogInfo log, int level)
    {
        return new HelperResult((writer) =>
        {
            if (log.Severity >= Model.Options.MinLevel &&
                (string.IsNullOrEmpty(Model.Options.NamePrefix) || log.Name.StartsWith(Model.Options.NamePrefix, StringComparison.Ordinal)))
            {

                WriteLiteralTo(writer, "        <tr class=\"logRow\">\r\n            <td>");
                WriteTo(writer, string.Format("{0:MM/dd/yy}", log.Time));

                WriteLiteralTo(writer, "</td>\r\n            <td>");
                WriteTo(writer, string.Format("{0:H:mm:ss}", log.Time));

                WriteLiteralTo(writer, $"</td>\r\n            <td title=\"{log.Name}\">");
                WriteTo(writer, log.Name);
                var severity = log.Severity.ToString().ToLowerInvariant();
                WriteLiteralTo(writer, $"</td>\r\n            <td class=\"{severity}\">");
                WriteTo(writer, log.Severity);

                WriteLiteralTo(writer, $"</td>\r\n            <td title=\"{log.Message}\"> \r\n");

                for (var i = 0; i < level; i++)
                {
                    WriteLiteralTo(writer, "                    <span class=\"tab\"></span>\r\n");
                }

                WriteLiteralTo(writer, "                ");
                WriteTo(writer, log.Message);

                WriteLiteralTo(writer, $"\r\n            </td>\r\n            <td title=\"{log.Exception}\">");

                WriteTo(writer, log.Exception);

                WriteLiteralTo(writer, "</td>\r\n        </tr>\r\n");

            }
        });
    }

    public HelperResult Traverse(ScopeNode node, int level, Dictionary<string, int> counts)
    {
        return new HelperResult((writer) => {
            // print start of scope
            WriteTo(writer, LogRow(new LogInfo()
            {
                Name = node.Name,
                Time = node.StartTime,
                Severity = LogLevel.Debug,
                Message = "Beginning " + node.State,
            }, level));

            var messageIndex = 0;
            var childIndex = 0;
            while (messageIndex < node.Messages.Count && childIndex < node.Children.Count)
            {
                if (node.Messages[messageIndex].Time < node.Children[childIndex].StartTime)
                {
                    WriteTo(writer, LogRow(node.Messages[messageIndex], level));

                    counts[node.Messages[messageIndex].Severity.ToString()]++;
                    messageIndex++;
                }
                else
                {
                    WriteTo(writer, Traverse(node.Children[childIndex], level + 1, counts));
                    childIndex++;
                }
            }
            if (messageIndex < node.Messages.Count)
            {
                for (var i = messageIndex; i < node.Messages.Count; i++)
                {
                    WriteTo(writer, LogRow(node.Messages[i], level));
                    counts[node.Messages[i].Severity.ToString()]++;
                }
            }
            else
            {
                for (var i = childIndex; i < node.Children.Count; i++)
                {
                    WriteTo(writer, Traverse(node.Children[i], level + 1, counts));
                }
            }
            // print end of scope
            WriteTo(writer, LogRow(new LogInfo()
            {
                Name = node.Name,
                Time = node.EndTime,
                Severity = LogLevel.Debug,
                Message = string.Format("Completed {0} in {1}ms", node.State, node.EndTime - node.StartTime)
            }, level));
        });
    }
}
@{ 
    Response.ContentType = "text/html";
}

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <title>ASP.NET Core Logs</title>
    <script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-2.1.1.min.js"></script>
    <style>
        <%$ include: LogPage.css %>
        <%$ include: Shared.css %>
    </style>
</head>
<body>
    <h1>ASP.NET Core Logs</h1>
    <form id="viewOptions" method="get">
        <select name="level">
            @foreach (var severity in Enum.GetValues(typeof(LogLevel)))
            {
                var severityInt = (int)severity;
                if ((int)Model.Options.MinLevel == severityInt)
                {
                    <option value="@severityInt" selected="selected">@severity</option>
                }
                else
                {
                    <option value="@severityInt">@severity</option>
                }
            }
        </select>
        <input type="text" name="name" value="@Model.Options.NamePrefix" />
        <input type="submit" value="filter" />
    </form>
    <form id="clear" method="post" action="">
        <button type="submit" name="clear" value="1">Clear Logs</button>
    </form>

    <table id="requestTable">
        <thead id="requestHeader">
            <tr>
                <th class="path">Path</th>
                <th class="method">Method</th>
                <th class="host">Host</th>
                <th class="statusCode">Status Code</th>
                <th class="logs">Logs</th>
            </tr>
        </thead>
        <colgroup>
            <col />
            <col />
            <col />
            <col />
            <col />
        </colgroup>
        @foreach (var activity in Model.Activities.Reverse())
        {
            <tbody>
                <tr class="requestRow">
                    @{
                        var activityPath = Model.Path.Value + "/" + activity.Id;
                        if (activity.HttpInfo != null)
                        {
                        	<td><a href="@activityPath" title="@activity.HttpInfo.Path">@activity.HttpInfo.Path</a></td>
                            <td>@activity.HttpInfo.Method</td>
                            <td>@activity.HttpInfo.Host</td>
                            <td>@activity.HttpInfo.StatusCode</td>
                        }
                        else if (activity.RepresentsScope)
                        {
                            <td colspan="4"><a href="@activityPath" title="@activity.Root.State">@activity.Root.State</a></td>
                        }
                        else
                        {
                            <td colspan="4"><a href="@activityPath">Non-scope Log</a></td>
                        }
                    }
                    <td class="logTd">
                        <table class="logTable">
                            <thead class="logHeader">
                                <tr class="headerRow">
                                    <th class="date">Date</th>
                                    <th class="time">Time</th>
                                    <th class="name">Name</th>
                                    <th class="severity">Severity</th>
                                    <th class="state">State</th>
                                    <th>Error<span class="collapse">^</span></th>
                                </tr>
                            </thead>
                            @{
                                var counts = new Dictionary<string, int>();
                                counts["Critical"] = 0;
                                counts["Error"] = 0;
                                counts["Warning"] = 0;                                
                                counts["Information"] = 0;
                                counts["Debug"] = 0;
                            }
                            <tbody class="logBody">
                                @if (!activity.RepresentsScope)
                                {
                                    // message not within a scope
                                    var logInfo = activity.Root.Messages.FirstOrDefault();
                                    @LogRow(logInfo, 0)
                                    counts[logInfo.Severity.ToString()] = 1;
                                }
                                else
                                {
                                    @Traverse(activity.Root, 0, counts)
                                }
                            </tbody>
                            <tbody class="summary">
                                <tr class="logRow">
                                    <td>@activity.Time.ToString("MM-dd-yyyy HH:mm:ss")</td>
                                    @foreach (var kvp in counts)
                                    {
                                        if (string.Equals("Debug", kvp.Key)) {
                                            <td>@kvp.Value @kvp.Key<span class="collapse">v</span></td>
                                        }
                                        else
                                        {
                                            <td>@kvp.Value @kvp.Key</td>
                                        }
                                    }
                                </tr>
                            </tbody>
                        </table>
                    </td>
                </tr>
            </tbody>
        }
    </table>
    <script type="text/javascript">
        $(document).ready(function () {
            $(".logBody").hide();
            $(".logTable > thead").hide();
            $(".logTable > thead").click(function () {
                $(this).closest(".logTable").find("tbody").hide();
                $(this).closest(".logTable").find(".summary").show();
                $(this).hide();
            });
            $(".logTable > .summary").click(function () {
                $(this).closest(".logTable").find("tbody").show();
                $(this).closest(".logTable").find("thead").show();
                $(this).hide();
            });
        });
    </script>
</body>
</html>